package org.developer.mvcc.app.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.developer.mvcc.app.service.vo.*;

import lombok.extern.slf4j.Slf4j;
import org.developer.mvcc.app.service.TransactionalService;
import org.developer.mvcc.util.TransactionIdGenerator;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 生成模板服务默认实现
 *
 * @author yisheng.mikelv@foxmail.com 2020/3/11 0:20
 */
@Service
@Slf4j
public class TransactionalServiceImpl implements TransactionalService {
    private List<Integer> activeTrxList = new CopyOnWriteArrayList<>();

    private Map<Integer, TableVO> tableDataMap = new ConcurrentHashMap<>();

    private UserInfoEntity userInfoEntity;

    private LinkedList<UndoLogVO> undoLogVOLinkedList = new LinkedList<>();

    private HashMap<Integer, ReadViewVO> trxReadviewMap = new HashMap<>();

    @Override
    public Integer beginTrx() {
        Integer nextTrxId = TransactionIdGenerator.nextTrxId();
        /**
         * 添加到活跃的事务列表中
         */
        activeTrxList.add(nextTrxId);

        /**
         * 当前事务开启readview
         */
        ReadViewVO readViewVO = new ReadViewVO();
        readViewVO.setActiveTrxIdSet(new ArrayList<>(activeTrxList));
        Collections.sort(readViewVO.getActiveTrxIdSet());
        readViewVO.setLowLimit(readViewVO.getActiveTrxIdSet().get(0));
        readViewVO.setHighLimit(TransactionIdGenerator.max() + 1);
        readViewVO.setCreatorTrxId(nextTrxId);

        trxReadviewMap.put(nextTrxId,readViewVO);

        return nextTrxId;
    }

    @Override
    public String initUserInfoTableMetaData() {
        TableVO tableVO = new TableVO();
        tableVO.setTableId(1);

        DataPageVO dataPageVO = new DataPageVO();
        dataPageVO.setTableVO(tableVO);
        dataPageVO.setOffset(1);

        TableRecordVO tableRecordVO = new TableRecordVO();
        tableRecordVO.setRecordId(111);

        UserInfoEntity content = new UserInfoEntity();
        content.setId(222);
        content.setName("zhangsan");
        tableRecordVO.setContent(content);

        /**
         * 设置到field方便修改
         */
        userInfoEntity = content;

        dataPageVO.setRecordVOList(Collections.singletonList(tableRecordVO));

        tableVO.setDataPages(Collections.singletonList(dataPageVO));

        String s = JSONObject.toJSONString(tableVO, SerializerFeature.PrettyFormat);
        log.info("init result:{}", s);
        return s;
    }

    @Override
    public void update(Integer trxId) {
        /**
         * 获取上一条undolog
         */
        UndoLogVO lastUndoLog = undoLogVOLinkedList.peekFirst();

        /**
         * 记录undo log
         */
        UndoLogVO undoLogVO = new UndoLogVO();

        undoLogVO.setLastUndoLog(lastUndoLog);
        undoLogVO.setTrxId(trxId);
        UserInfoEntity entity = new UserInfoEntity();
        entity.setId(userInfoEntity.getId());
        entity.setName(userInfoEntity.getName());
        undoLogVO.setContentOfTheTrx(entity);

        /**
         * 将undo log，链接到undo log list
         */
        undoLogVOLinkedList.addFirst(undoLogVO);

        /**
         * 修改记录
         */
        this.userInfoEntity.setName(this.userInfoEntity.getName() + "-trx-" + trxId);

        /**
         * 修改记录上的指针，指向当前事务
         */
        this.userInfoEntity.setTrxId(trxId);

        /**
         * 在当前行上，设置上一条的回滚日志
         */
        this.userInfoEntity.setUndoLogVO(lastUndoLog);
    }

    @Override
    public UserInfoEntity select(Integer trxId) {
        ReadViewVO readViewVO = trxReadviewMap.get(trxId);
        if (readViewVO == null) {
            throw new RuntimeException();
        }

        UserInfoEntity entity = this.userInfoEntity;
        while (entity != null) {
            Integer entityTrxId = entity.getTrxId();
            /**
             * 如果就是自己改的，那肯定可以看
             */
            if (readViewVO.getCreatorTrxId().equals(entityTrxId)) {
                return entity;
            }

            if (entityTrxId < readViewVO.getLowLimit()) {
                return entity;
            }

            /**
             * 判读是否要找上个版本
             */
            Boolean getLastRecord = false;
            if (entityTrxId >= readViewVO.getHighLimit()) {
                getLastRecord = true;
            }

            if (readViewVO.getActiveTrxIdSet().contains(entityTrxId)) {
                getLastRecord = true;
            }

            if (getLastRecord) {
                UndoLogVO undoLogVO = entity.getUndoLogVO();
                if (undoLogVO == null) {
                    /**
                     * 都没有undo log了，说明是肯定已经提交了的
                     */
                    return entity;
                }
                entity = undoLogVO.getContentOfTheTrx();
            }
        }

        return null;
    }

    @Override
    public void commit(Integer trxId) {
        activeTrxList.remove(trxId);
        log.info("commit {},left trx:{}",activeTrxList);
    }


}
